use std::path::PathBuf;

use axum::extract::State;
use axum::{Json, Router, routing::post};
use axum_ifnk_common::{
    app_error,
    prelude::{AppError, Response},
};
use serde::Deserialize;
use utoipa::ToSchema;

use crate::{
    AppState,
    sound_wave_reader_service::{
        SoundWaveAllChannels, SoundWaveReaderService, SoundWaveSingleChannel,
    },
};

pub fn routes() -> Router<AppState> {
    Router::new()
        .route("/soundwave/all", post(read_all_channels))
        .route("/soundwave/channel", post(read_single_channel))
}

#[derive(Deserialize, ToSchema)]
pub struct ReadAllChannelsRequest {
    pub file_path: String,
    pub smprate: u32,
    pub channels: usize,
    pub depth: u32,
    #[schema(example = "int")]
    pub symbol_type: String,
    pub offset: f64,
    pub duration: f64,
    #[serde(default = "default_coef")]
    #[schema(default = 1932735283.20)]
    pub coef: f32,
    #[serde(default = "default_range")]
    #[schema(default = 10.0)]
    pub range: f32,
    #[serde(default)]
    #[schema(default = false)]
    pub convert_to_vol: bool,
}

#[derive(Deserialize, ToSchema)]
pub struct ReadSingleChannelRequest {
    pub file_path: String,
    pub smprate: u32,
    pub channels: usize,
    pub depth: u32,
    #[schema(example = "int")]
    pub symbol_type: String,
    pub offset: f64,
    pub duration: f64,
    #[serde(default = "default_channel_index")]
    #[schema(default = 1)]
    pub channel_index: usize,
    #[serde(default = "default_coef")]
    #[schema(default = 1932735283.20)]
    pub coef: f32,
    #[serde(default = "default_range")]
    #[schema(default = 10.0)]
    pub range: f32,
    #[serde(default)]
    #[schema(default = false)]
    pub convert_to_vol: bool,
}

#[utoipa::path(
    post,
    path = "/soundwave/all",
    request_body = ReadAllChannelsRequest,
    responses(
        (status = 200, description = "读取声波所有通道成功", body = Response<SoundWaveAllChannels>)
    ),
    tag = "声波读取"
)]
async fn read_all_channels(
    State(state): State<AppState>,
    Json(payload): Json<ReadAllChannelsRequest>,
) -> Result<Response<SoundWaveAllChannels>, AppError> {
    let result = read_all_channels_impl(&state.sound_wave_reader, payload)
        .map_err(|err| app_error!(500, "读取声波所有通道数据失败: {}", err))?;

    Ok(Response::success(result))
}

#[utoipa::path(
    post,
    path = "/soundwave/channel",
    request_body = ReadSingleChannelRequest,
    responses(
        (status = 200, description = "读取声波单通道成功", body = Response<SoundWaveSingleChannel>)
    ),
    tag = "声波读取"
)]
async fn read_single_channel(
    State(state): State<AppState>,
    Json(payload): Json<ReadSingleChannelRequest>,
) -> Result<Response<SoundWaveSingleChannel>, AppError> {
    let result = read_single_channel_impl(&state.sound_wave_reader, payload)
        .map_err(|err| app_error!(500, "读取声波单通道数据失败: {}", err))?;

    Ok(Response::success(result))
}

fn read_all_channels_impl(
    service: &std::sync::Arc<SoundWaveReaderService>,
    payload: ReadAllChannelsRequest,
) -> anyhow::Result<SoundWaveAllChannels> {
    let path = PathBuf::from(payload.file_path);
    service.read_sound_wave_all_channels_data(
        &path,
        payload.smprate,
        payload.channels,
        payload.depth,
        payload.symbol_type.as_str(),
        payload.offset,
        payload.duration,
        payload.coef,
        payload.range,
        payload.convert_to_vol,
    )
}

fn read_single_channel_impl(
    service: &std::sync::Arc<SoundWaveReaderService>,
    payload: ReadSingleChannelRequest,
) -> anyhow::Result<SoundWaveSingleChannel> {
    let path = PathBuf::from(payload.file_path);
    service.read_sound_wave_data_by_channel(
        &path,
        payload.smprate,
        payload.channels,
        payload.depth,
        payload.symbol_type.as_str(),
        payload.offset,
        payload.duration,
        payload.channel_index,
        payload.coef,
        payload.range,
        payload.convert_to_vol,
    )
}

fn default_coef() -> f32 {
    1_932_735_283.20_f32
}

fn default_range() -> f32 {
    10.0
}

fn default_channel_index() -> usize {
    1
}
